﻿using System.Collections.Generic;
using System.Net.Sockets;
using Nutilities.Net;
using SharedLibrary;

namespace Auktionshuset
{
    public class Connection : ThreadedSocket
    {
        public delegate void AuctionEvent(Auction auction);
        public event AuctionEvent AuctionPosted;

        public User User { get; private set; }

        public Dictionary<int, AuctionWindow> AuctionWindows { get; set; }

        public Connection(Socket socket) : base(socket)
        {
        }

        protected override void MessageReceived(byte message)
        {
            switch (message)
            {
                case Command.UPDATE_AUCTION:
                    HandleUpdateAuction();
                    break;
                case Command.POST_AUCTION:
                    HandlePostAuction();
                    break;
            }
        }

        // Requests - Controls output
        private Response GetResponse()
        {
            if (Reader.ReadByte() == Command.ERROR)
            {
                return new Response(false, Reader.ReadString());
            }
            return new Response(true);
        }

        public Response AttemptLogin(string username, string password)
        {
            Writer.Write(Command.LOGIN);
            Writer.Write(username);
            Writer.Write(password);
            Writer.Flush();

            Response resp = GetResponse();
            User = resp.Success ? User.ReadFrom(Reader) : null;

            return resp;
        }

        public void Logout()
        {
            Writer.Write(Command.LOGOUT);
            Writer.Flush();
        }

        public Response AttemptRegister(string username, string password, string alias)
        {
            Writer.Write(Command.REGISTER);
            Writer.Write(username);
            Writer.Write(password);
            Writer.Write(alias);
            Writer.Flush();

            return GetResponse();
        }

        public void PostAuction(Auction auction)
        {
            Writer.Write(Command.POST_AUCTION);
            auction.WriteTo(Writer);
            Writer.Flush();
        }

        public void RequestAuctionUpdate(Auction auction)
        {
            Writer.Write(Command.UPDATE_AUCTION);
            Writer.Write(auction.Id);
            Writer.Flush();
        }

        public List<Auction> GetAuctions()
        {
            Writer.Write(Command.GET_AUCTIONS);
            Writer.Flush();

            return BinaryReaderUtility.ReadList(Reader, Auction.ReadFrom);
        }

        public void SubscribeToAuction(AuctionWindow aw)
        {
            AuctionWindows.Add(aw.Auction.Id, aw);

            Writer.Write(Command.AUCTION_SUBSCRIBE);
            Writer.Write(aw.Auction.Id);
            Writer.Flush();
        }

        public void UnsubscribeToAuction(AuctionWindow aw)
        {
            Writer.Write(Command.AUCTION_UNSUBSCRIBE);
            Writer.Write(aw.Auction.Id);
            Writer.Flush();

            AuctionWindows.Remove(aw.Auction.Id);
        }


        // Message Handlers - Responds to input
        private void HandlePostAuction()
        {
            Auction auction = Auction.ReadFrom(Reader);
            OnAuctionPosted(auction);
        }

        protected virtual void OnAuctionPosted(Auction auction)
        {
            AuctionEvent handler = AuctionPosted;
            if (handler != null) handler(auction);
        }

        public void SendPlaceBid(int auctionId, decimal bid)
        {
            Writer.Write(Command.PLACE_BID);
            Writer.Write(auctionId);
            Writer.Write(bid);
            Writer.Flush();
        }

        private void HandleUpdateAuction()
        {
            int auctionId = Reader.ReadInt32();

            if (AuctionWindows.ContainsKey(auctionId))
            {
                Auction auction = AuctionWindows[auctionId].Auction;
                auction.UpdateFrom(Reader);

                AuctionWindows[auctionId].Refresh();
            }
        }
    }

    public class Response
    {
        public bool Success { get; private set; }
        public string Error { get; private set; }

        public Response(bool success)
        {
            Success = success;
        }

        public Response(bool success, string error)
            : this(success)
        {
            Error = error;
        }
    }
}
